#pragma once

#include <FTPuser.h>
#include <reply.h>
#include <TcpConnection.h>
#include <event2/listener.h>
#include <InetAddress.h>
#include <fmt/format.h>
#include <filesystem>
#include <algorithm>
#include <map>

constexpr int MAX_DATA_CONNECTION_NUM_PER_USER = 3;

enum FileState {
	Reading = (1 << 0),     /** 文件正在被读取 */
	Writing = (1 << 1),		/** 文件正在被写入 */
	ReadingOrWriting = (Reading | Writing)
};

constexpr int BLOCK_SIZE = 1024 * 4; // 4KB

struct FTPFile {
	size_t offset_;
	size_t fileSize_;
	FILE* file_;
	std::string filename_;

	FTPFile() :
		filename_(""),
		offset_(0),
		fileSize_(0),
		file_(nullptr)
	{}

	bool isOpen() {
		return file_ != nullptr;
	}

	bool open(const std::string& file, const std::string& mode) {
		filename_ = file;
		if (std::filesystem::exists(file)) {
			fileSize_ = std::filesystem::file_size(file);
		}
		file_ = ::fopen(filename_.c_str(), mode.c_str());
		if (file_ == nullptr) {
			perror("fopen");
			return false;
		}
		// 设置偏移量，用于断点重传
		int res = std::fseek(file_, offset_, SEEK_SET) == 0;
		offset_ = 0; // 防止不同文件打开时重复使用offset_
		return res;
		//// 获取文件大小
		//std::fseek(file_, 0, SEEK_END);
		//fileSize_ = std::ftell(file_);
		//if (mode.find("a") != std::string::npos) {
		//	std::fseek(file_, 0, SEEK_SET);
		//}
		//return true;
	}

	bool close() {
		if (file_ != nullptr) {
			std::fflush(file_);
			std::fclose(file_);
		}
		file_ = nullptr;
		offset_ = 0;
		fileSize_ = 0;
		filename_ = "";
		return true;
	}

	int read(char* readBuf, size_t size) {
		size_t n = std::fread(readBuf, sizeof(char), size, file_);
		offset_ += n;
		return n;
	}

	int write(const char* writeBuf, size_t size) {
		size_t n = std::fwrite(writeBuf, sizeof(char), size, file_);
		offset_ += n;
		return n;
	}

	~FTPFile() {
		close();
	}

	bool isWriteFinish() {
		return offset_ == fileSize_;
	}

	void setOffset(size_t offset) {
		offset_ = offset;
	}
};

class FTPSession : public std::enable_shared_from_this<FTPSession> {
	enum class State
	{
		UNLOGIN = 0, 
		LOGINING,
		LOGINED, 
	};
public:
	FTPSession(const std::shared_ptr<TcpConnection>& conn);

	std::string name() { return name_; }

	bool parseCmd(const std::string& cmdline);

	bool handleCmd(std::string cmd, const std::string& param);

	~FTPSession();
private:
	void send(int code, const std::string& msg);

	int send(const FTPFile& flie);

	int recv(char* buf,int maxLen);
	
	int recv(const FTPFile& flie);

	bool checkLoginState();

	bool checkPermission(const std::string filePath);

	bool handleUSER(const std::string& param);
	
	bool handlePASS(const std::string& param);
	
	bool handlePASV(const std::string& param);

	bool handleTYPE(const std::string& param);

	bool handleSTRU(const std::string& param);

	bool handleMODE(const std::string& param);
	
	bool handleLIST(const std::string& param);

	bool handleSTOR(const std::string& param);
	
	bool handleRETR(const std::string& param);

	bool handleSIZE(const std::string& param);

	bool handleREST(const std::string& param);

	bool handlePWD(const std::string& param);

	bool handleNOOP(const std::string& param);

	bool handleMKD(const std::string& param);

	bool handleRNFR(const std::string& param);

	bool handleRNTO(const std::string& param);

	bool handleDELE(const std::string& param);

	bool handleHELP(const std::string& param);

	void listenCallback(struct evconnlistener*, socket_t, struct sockaddr*, int socklen, void*);
	void writeFileCallback(const std::shared_ptr<TcpConnection>& conn);

	void readFileCallback(const std::shared_ptr<TcpConnection>& conn, Buffer& inBuffer);

	void startSendFile();
	
	void startRecvFile(Buffer& inBuffer);

private:
	std::unordered_map<std::string, int> fileStateMap_;
	bool isTransffering_; // 正在传输数据
	std::string name_;	// 会话名称
	std::unique_ptr<FTPUser> user_;
	std::unique_ptr<FTPFile> file_;
	std::string lastCommand_; // 上一条指令
	char* buffer_{ new char[BLOCK_SIZE] };
	State loginState_;
	std::shared_ptr<TcpConnection> cmdConn_; // 控制连接，listenCallback后由TcpServer设置
	InetAddress dataListenAddr_; // 数据连接监听端口
	std::unique_ptr<evconnlistener, std::function<void(evconnlistener*)>> dataAcceptor_; // 数据连接监听器
	std::shared_ptr<TcpConnection> dataConn_; // 仅保留最近一次数据连接
};